
public class ST {

	ASTTreeNode astRoot;
	STNode stRoot;
	
	public static void main(String[] args) {
		p1 parser = new p1(args[0]);//"c:\\matrix_order1");
		ST st = new ST();
		
		st.astRoot = parser.getASTTree();
		st.stRoot = st.standardize(st.astRoot);
		st.stRoot.print(0);
	}

	STNode letNode(ASTTreeNode node){
		assert(node.content.equalsIgnoreCase("let")) : "letNode: the ast node passed my be an let node";
		assert(node.childArray.length==2):"letNode: let node can have only two children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode rightChild = standardize(node.childArray[1]);
		assert (leftChild != null) : "should not be null";
		assert (rightChild != null) : "should not be null";
		assert(leftChild.content.equalsIgnoreCase("=")) : "letNode: left child not '='";
		STNode gamma = new STNode("gamma", 2);
		STNode lambda = new STNode("lambda", 2);
		STNode x = leftChild.childArray[0];
		STNode e = leftChild.childArray[1];
		STNode p = rightChild;
		gamma.childArray[0] = lambda;
		gamma.childArray[1] = e;
		lambda.childArray[0] = x;
		lambda.childArray[1] = p;
		return gamma;
	}

	STNode whereNode(ASTTreeNode node){
		assert(node.content.equalsIgnoreCase("where")) : "whereNode: the ast node passed my be an where node";
		assert(node.childArray.length==2):"whereNode: where node can have only two children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode rightChild = standardize(node.childArray[1]);
		assert(rightChild.content.equalsIgnoreCase("=")) : "whereNode: left child not '='";
		STNode gamma = new STNode("gamma", 2);
		STNode lambda = new STNode("lambda", 2);
		STNode p = leftChild;
		STNode x = rightChild.childArray[0];
		STNode e = rightChild.childArray[1];
		gamma.childArray[0] = lambda;
		gamma.childArray[1] = e;
		lambda.childArray[0] = x;
		lambda.childArray[1] = p;
		return gamma;
	}

	private STNode equalNode(ASTTreeNode node) {
		assert(node.content.equalsIgnoreCase("=")) : "equalNode: the ast node must be an equal node";
		assert(node.childArray.length==2):"gammaNode:  node can have only two children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode rightChild = standardize(node.childArray[1]);
		STNode equal = new STNode("=", 2);
		equal.childArray[0] = leftChild;
		equal.childArray[1] = rightChild;
		return equal;
	}
	
	private STNode gammaNode(ASTTreeNode node) {
		assert(node.content.equalsIgnoreCase("gamma")) : "gammaNode: the ast node must be an right node";
		assert(node.childArray.length==2):"gammaNode: where node can have only two children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode rightChild = standardize(node.childArray[1]);
		STNode gamma = new STNode("gamma", 2);
		gamma.childArray[0] = leftChild;
		gamma.childArray[1] = rightChild;
		return gamma;
	}
	
	private STNode lambdaNode(ASTTreeNode node) {
		assert(node.content.equalsIgnoreCase("lambda")) : "lambda: the ast node must be an right node";
		assert(node.childArray.length==2):"lambda: where node can have only two children";
		//STNode leftChild = new STNode(node.childArray[0].content,0);
		STNode leftChild = standardize(node.childArray[0]);
		STNode rightChild = standardize(node.childArray[1]);
		STNode lambda = new STNode("lambda", 2);
		lambda.childArray[0] = leftChild;
		lambda.childArray[1] = rightChild;
		return lambda;
	}

	private STNode withinNode(ASTTreeNode node) {
		assert(node.content.equalsIgnoreCase("within")) : "within: the ast node passed my be an within node";
		assert(node.childArray.length==2):"within: within node can have only two children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode rightChild = standardize(node.childArray[1]);
		assert(leftChild.content.equalsIgnoreCase("=")) : "whereNode: left child not '='";
		assert(rightChild.content.equalsIgnoreCase("=")) : "whereNode: left child not '='";
		
		STNode equal = new STNode("=", 2);
		STNode gamma = new STNode("gamma", 2);
		STNode lambda = new STNode("lambda", 2);
		STNode x1 = leftChild.childArray[0];
		STNode e1 = leftChild.childArray[1];
		STNode x2 = rightChild.childArray[0];
		STNode e2 = rightChild.childArray[1];
		
		equal.childArray[0] = x2;
		equal.childArray[1] = gamma;
		gamma.childArray[0] = lambda;
		gamma.childArray[1] = e1;
		lambda.childArray[0] = x1;
		lambda.childArray[1] = e2;
		return equal;
	}
	
	private STNode fcnFormNode(ASTTreeNode node) {
		assert(node.content.equalsIgnoreCase("function_form")) : "fcn_form: the ast node passed my be an fcn_form node";
		ASTTreeNode astLeftChild = node.childArray[0];
		assert(astLeftChild.content.contains("<ID:")) : "fcn_form: the ast node passed my be an fcn_form node";
		int numParam = node.childArray.length - 2;
		
		STNode equal = new STNode("=", 2);
		STNode p = new STNode(node.childArray[0].content, 0);
		STNode funParam[] = new STNode[numParam];
		STNode funLambda[] = new STNode[numParam];
		for(int i = 0 ; i < numParam ; i++){
			//funParam[i] = new STNode(node.childArray[i+1].content,0);
			if (node.childArray[i+1].content.equals(",")){
				int k = 9;k++;
			}
			funParam[i] = standardize(node.childArray[i+1]);
			funLambda[i] = new STNode("lambda",2);
		}
		STNode e = standardize(node.childArray[node.childArray.length-1]);
		
		
		equal.childArray[0] = p;
		equal.childArray[1] = funLambda[0];
		funLambda[0].childArray[0] = funParam[0];
		
		if (numParam == 1){
			funLambda[0].childArray[1] = e;
		}else if (numParam >= 2){
			funLambda[0].childArray[1] = funLambda[1];
			for(int i = 1 ; i < (funParam.length-1) ; i++){
				funLambda[i].childArray[0] = funParam[i];
				funLambda[i].childArray[1] = funLambda[i+1];
			}
			funLambda[numParam-1].childArray[0] = funParam[numParam-1];
			funLambda[numParam-1].childArray[1] = e;
		}
		return equal;
	}
	
	/*
	 * We will not optimize, all binary operators,
	 * also 'comma' node too,
	 */
	private STNode tauCommaNode(ASTTreeNode node) {
		assert(node.content.equalsIgnoreCase("tau") | 
				node.content.equalsIgnoreCase(",")) : "tauCommaNode: the ast node must be an right node";
		int numOfchildren = node.childArray.length;
		STNode tau = new STNode(node.content, numOfchildren);
		STNode children[] = new STNode[numOfchildren]; 
		for(int i = 0 ; i < numOfchildren ; i++){
			children[i] = standardize(node.childArray[i]);
			tau.childArray[i] = children[i];
		}
		return tau;
	}
	
	private STNode binaryOpNode(ASTTreeNode node) {
		assert(node.childArray.length==2):"binaryOP:  node can have only two children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode rightChild = standardize(node.childArray[1]);
		STNode binaryOP = new STNode(node.content, 2);
		binaryOP.childArray[0] = leftChild;
		binaryOP.childArray[1] = rightChild;
		return binaryOP;
	}
	
	private STNode uniaryOPNode(ASTTreeNode node) {
		assert(node.childArray.length==1):"unaryOP:  node can have only one children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode unaryOP = new STNode(node.content, 1);
		unaryOP.childArray[0] = leftChild;
		return unaryOP;
	}
	
	private STNode ternaryOPNode(ASTTreeNode node) {
		assert(node.childArray.length==3):"ternaryOP:  node can have only two children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode middleChild = standardize(node.childArray[1]);
		STNode rightChild = standardize(node.childArray[2]);
		STNode ternaryOP = new STNode(node.content, 3);
		ternaryOP.childArray[0] = leftChild;
		ternaryOP.childArray[1] = middleChild;
		ternaryOP.childArray[2] = rightChild;
		return ternaryOP;
	}
	
	private STNode infixOPNode(ASTTreeNode node) {
		assert(node.childArray.length==3):"ternaryOP:  node can have only two children";
		STNode leftChild = standardize(node.childArray[0]);
		STNode middleChild = standardize(node.childArray[1]);
		STNode rightChild = standardize(node.childArray[2]);
		
		STNode gamma1 = new STNode("gamma", 2);
		STNode gamma2 = new STNode("gamma", 2);
		
		gamma1.childArray[0] = gamma2;
		gamma1.childArray[1] = rightChild;
		gamma2.childArray[0] = middleChild;
		gamma2.childArray[1] = leftChild;
		
		return gamma1;
	}

	/*
	 * Complete And node
	 */
	private STNode andNode(ASTTreeNode node) {
		assert(node.content.equalsIgnoreCase("and")) : "andNode: the ast node must be an right node";
		int numOfchildren = node.childArray.length;
		STNode tau = new STNode("tau", numOfchildren);
		STNode comma = new STNode(",", numOfchildren);
		
		for(int i = 0 ; i < numOfchildren ; i++){
			STNode temp = standardize(node.childArray[i]);
			comma.childArray[i] = temp.childArray[0];
			tau.childArray[i] = temp.childArray[1];
		}
		
		STNode equal = new STNode("=", 2);
		equal.childArray[0] = comma;
		equal.childArray[1] = tau;
		
		return equal;
	}

	private STNode recNode(ASTTreeNode node) {
		assert(node.content.equalsIgnoreCase("rec")) : "recNode: the ast node must be an right node";
		assert(node.childArray.length==1):"recOP:  node can have only two children";
		STNode singleChild = standardize(node.childArray[0]);
		assert(singleChild.content.equalsIgnoreCase("=")) : "recNode: must standardize to '='";
		
		STNode equal = new STNode("=", 2);
		STNode gamma = new STNode("gamma", 2);
		STNode lambda = new STNode("lambda", 2);
		STNode ystar = new STNode("<Y*>", 0);
		STNode x1 = singleChild.childArray[0];
		STNode x2 = new STNode(x1.content, 0);
		STNode e = singleChild.childArray[1];
		
		equal.childArray[0] = x1;
		equal.childArray[1] = gamma;
		gamma.childArray[0] = ystar;
		gamma.childArray[1] = lambda;
		lambda.childArray[0] = x2;
		lambda.childArray[1] = e;
		
		return equal;
	}

	
	STNode standardize(ASTTreeNode astTreeNode) {
		if (astTreeNode.content.equalsIgnoreCase("let")){
			return letNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("where")){
			return whereNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("within")){
			return withinNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("function_form")){
			return fcnFormNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("=")){
			return equalNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("tau") | 
				astTreeNode.content.equalsIgnoreCase(",")){
			return tauCommaNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("and")){
			return andNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("not") | 
				astTreeNode.content.equalsIgnoreCase("neg")){
			return uniaryOPNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("aug") | 
				astTreeNode.content.equalsIgnoreCase("or")  |
				astTreeNode.content.equalsIgnoreCase("&")  |
				astTreeNode.content.equalsIgnoreCase("gr")  |
				astTreeNode.content.equalsIgnoreCase("ge")  |
				astTreeNode.content.equalsIgnoreCase("ls")  |
				astTreeNode.content.equalsIgnoreCase("le")  |
				astTreeNode.content.equalsIgnoreCase("eq")  |
				astTreeNode.content.equalsIgnoreCase("ne")  |
				astTreeNode.content.equalsIgnoreCase("+")  |
				astTreeNode.content.equalsIgnoreCase("-")  |
				astTreeNode.content.equalsIgnoreCase("/")  |
				astTreeNode.content.equalsIgnoreCase("*")  |
				astTreeNode.content.equalsIgnoreCase("**") ){
			return binaryOpNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("->")){
			return ternaryOPNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("@")){
			return infixOPNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("rec")){
			return recNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("gamma")){
			return gammaNode(astTreeNode);
		}else if (astTreeNode.content.equalsIgnoreCase("lambda")){
			return lambdaNode(astTreeNode);
		}else if (astTreeNode.terminal == true){
			return new STNode(astTreeNode.content,0);
		}else{
			System.out.println("standarize: this state should not be reached");
			System.exit(-1);
			return null;
		}
	}

	
}
